package prepare

import (
	"errors"
	"fmt"
	"reflect"
)

var (
	repoRegister           = []func(){}
	factoryRegister        = []func(){}
	serviceRegister        = []func(){}
	authControllerRegister = []func(){}
	controllerRegister     = []func(){}
	natControllerRegister  = []func(){}

	dependency     = map[reflect.Type]interface{}{}
	authContorller = map[reflect.Type]interface{}{}
	contorller     = map[reflect.Type]interface{}{}
	natsContorller = map[reflect.Type]interface{}{}
)

func Register() {
	for _, v := range repoRegister {
		v()
	}

	for _, v := range factoryRegister {
		v()
	}

	for _, v := range serviceRegister {
		v()
	}

	for _, v := range authControllerRegister {
		v()
	}

	for _, v := range controllerRegister {
		v()
	}

	for _, v := range natControllerRegister {
		v()
	}
}

func RegisterRepo(f func()) {
	repoRegister = append(repoRegister, f)
}

func RegisterService(f func()) {
	serviceRegister = append(serviceRegister, f)
}

func RegisterFactory(f func()) {
	factoryRegister = append(factoryRegister, f)
}

func RegisterAuthController(f func()) {
	authControllerRegister = append(authControllerRegister, f)
}

func RegisterController(f func()) {
	controllerRegister = append(controllerRegister, f)
}

func RegisterNatsController(f func()) {
	natControllerRegister = append(natControllerRegister, f)
}

func InjectDependency(obj interface{}) {
	ftype := reflect.TypeOf(obj)
	if ftype.Kind() != reflect.Func {
		panic(errors.New("It's not a func"))
	}
	if ftype.NumOut() != 1 {
		panic(errors.New("Return must be an object pointer"))
	}
	outType := ftype.Out(0)
	if outType.Kind() != reflect.Interface && outType.Kind() != reflect.Ptr {
		panic(errors.New("Return must be an object pointer or interface"))
	}
	dependency[outType] = obj

	//fmt.Printf("\ninject dependency %v %v\n", outType, obj)
}

func FetchDependency(obj interface{}) {
	if reflect.ValueOf(obj).Kind() != reflect.Ptr {
		panic(fmt.Errorf("input must be an pointer to interface or object ptr"))
	}

	f, ok := dependency[reflect.ValueOf(obj).Elem().Type()]
	if !ok {
		panic(fmt.Errorf("dependency %v not inject", reflect.ValueOf(obj).Elem().Type()))
	}
	res := reflect.ValueOf(f).Call([]reflect.Value{})
	reflect.ValueOf(obj).Elem().Set(res[0])

	//fmt.Printf("\nfetch dependency %v %v\n", reflect.ValueOf(obj).Elem().Type(), res[0].Type())
}

func InjectAuthController(obj interface{}) {
	ftype := reflect.TypeOf(obj)
	if ftype.Kind() != reflect.Func {
		panic(errors.New("It's not a func"))
	}
	if ftype.NumOut() != 1 {
		panic(errors.New("Return must be an object pointer"))
	}
	outType := ftype.Out(0)
	if outType.Kind() != reflect.Interface && outType.Kind() != reflect.Ptr {
		panic(errors.New("Return must be an object pointer or interface"))
	}
	authContorller[outType] = obj

	//fmt.Printf("\ninject dependency %v %v\n", outType, obj)
}

func InjectController(obj interface{}) {
	ftype := reflect.TypeOf(obj)
	if ftype.Kind() != reflect.Func {
		panic(errors.New("It's not a func"))
	}
	if ftype.NumOut() != 1 {
		panic(errors.New("Return must be an object pointer"))
	}
	outType := ftype.Out(0)
	if outType.Kind() != reflect.Interface && outType.Kind() != reflect.Ptr {
		panic(errors.New("Return must be an object pointer or interface"))
	}
	contorller[outType] = obj

	//fmt.Printf("\ninject dependency %v %v\n", outType, obj)
}

func InjectNatsController(obj interface{}) {
	ftype := reflect.TypeOf(obj)
	if ftype.Kind() != reflect.Func {
		panic(errors.New("It's not a func"))
	}
	if ftype.NumOut() != 1 {
		panic(errors.New("Return must be an object pointer"))
	}
	outType := ftype.Out(0)
	if outType.Kind() != reflect.Interface && outType.Kind() != reflect.Ptr {
		panic(errors.New("Return must be an object pointer or interface"))
	}
	natsContorller[outType] = obj

	//fmt.Printf("\ninject dependency %v %v\n", outType, obj)
}

func FetchAllAuthControllers() map[reflect.Type]interface{} {
	cs := make(map[reflect.Type]interface{}, len(authContorller))
	for tp, f := range authContorller {
		res := reflect.ValueOf(f).Call([]reflect.Value{})
		cs[tp] = res[0].Interface()
	}
	return cs
}

func FetchAllControllers() map[reflect.Type]interface{} {
	cs := make(map[reflect.Type]interface{}, len(contorller))
	for tp, f := range contorller {
		res := reflect.ValueOf(f).Call([]reflect.Value{})
		cs[tp] = res[0].Interface()
	}
	return cs
}

func FetchAllNatsControllers() map[reflect.Type]interface{} {
	cs := make(map[reflect.Type]interface{}, len(natsContorller))
	for tp, f := range natsContorller {
		res := reflect.ValueOf(f).Call([]reflect.Value{})
		cs[tp] = res[0].Interface()
	}
	return cs
}

func FetchController(obj interface{}) {
	if reflect.ValueOf(obj).Kind() != reflect.Ptr {
		panic(fmt.Errorf("input must be an pointer to interface or object ptr"))
	}

	f, ok := authContorller[reflect.ValueOf(obj).Elem().Type()]
	if !ok {
		panic(fmt.Errorf("dependency %v not inject", reflect.ValueOf(obj).Elem().Type()))
	}
	res := reflect.ValueOf(f).Call([]reflect.Value{})
	reflect.ValueOf(obj).Elem().Set(res[0])

	//fmt.Printf("\nfetch dependency %v %v\n", reflect.ValueOf(obj).Elem().Type(), res[0].Type())
}
